package fr.vvins.core.biz.catalog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.beanutils.BeanComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.googlecode.ehcache.annotations.Cacheable;

import fr.vvins.core.biz.catalog.dto.VigneronDto;
import fr.vvins.core.biz.catalog.dto.VigneronDtoInfo;
import fr.vvins.core.biz.catalog.dto.VinDto;
import fr.vvins.core.biz.catalog.dto.VinDtoInfo;
import fr.vvins.core.biz.search.SearchException;
import fr.vvins.core.biz.search.SearchManager;
import fr.vvins.core.biz.search.dto.SearchResultDto;
import fr.vvins.core.catalog.entity.ListContent;
import fr.vvins.core.catalog.entity.ListQuery;
import fr.vvins.core.catalog.entity.Vigneron;
import fr.vvins.core.catalog.entity.Vin;
import fr.vvins.core.catalog.entity.dao.ListContentRepository;
import fr.vvins.core.catalog.entity.dao.VigneronRepository;
import fr.vvins.core.catalog.entity.dao.VinRepository;
import fr.vvins.core.common.TechnicalException;
import fr.vvins.core.common.dto.BeanDtoInfo;
import fr.vvins.core.common.dto.DtoFactory;
import fr.vvins.core.common.dto.EntityDtoConverter;


public class CatalogManagerImpl implements CatalogManager {
	
	private final static Logger log = LoggerFactory.getLogger(CatalogManagerImpl.class);
	
	@Autowired
	private VigneronRepository vigneronDao;

	@Autowired
	private VinRepository vinDao;

	@Autowired
	private ListContentRepository listContentDao;
	
	@Autowired
	private DtoFactory dtoFactory;
	
	@Autowired
	private EntityDtoConverter converter;
	
	@Autowired
	private SearchManager searchManager;
	
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public List<VigneronDtoInfo> getVignerons() throws CatalogException, TechnicalException{
		Iterable<Vigneron> listEntity;
		try {
			listEntity = vigneronDao.findAll();
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE,e);
		}
		if(listEntity == null){
			return null;
		}
		List<VigneronDtoInfo> dtos = new ArrayList<VigneronDtoInfo>();
		for(Vigneron v : listEntity){
			VigneronDtoInfo dto = new VigneronDtoInfo();
			converter.convertEntityToDto(v, dto);
			dtos.add(dto);
		}
		return dtos;
	}
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public VigneronDto getVigneron(int vigneronId) throws CatalogException, TechnicalException {
		VigneronDto vigneron = new VigneronDto();
		try {
			Vigneron entity = vigneronDao.findOne(vigneronId);
			converter.convertEntityToDto(entity, vigneron);
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return vigneron;
	}
	
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public VigneronDto getVigneronByContent(int contentId) throws CatalogException, TechnicalException {
		VigneronDto vigneron = new VigneronDto();
		try {
			Vigneron entity = vigneronDao.findByContent(contentId);
			converter.convertEntityToDto(entity, vigneron);
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return vigneron;
	}
	
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public VigneronDto getVigneronByKey(String key) throws CatalogException, TechnicalException {
		VigneronDto vigneron = new VigneronDto();
		try {
			Vigneron entity = vigneronDao.findByContentKey(key);
			converter.convertEntityToDto(entity, vigneron);
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return vigneron;
	}
	
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public Set<VinDtoInfo> getVinsVigneron(int vigneronId) throws CatalogException, TechnicalException {
		Set<VinDtoInfo> dtos = null;
		try {
			Vigneron entity = vigneronDao.findOne(vigneronId);
			Set<Vin> vins = entity.getVins();
			dtos = new HashSet<VinDtoInfo>(vins.size());
			if(vins != null){
				for(Vin v : vins){
					VinDtoInfo dto = new VinDtoInfo();
					converter.convertEntityToDto(v, dto);
					dtos.add(dto);
				}
			}
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return dtos;
	}
	

	
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public Set<VinDtoInfo> getVinsVigneronByContent(int contentId) throws CatalogException, TechnicalException {
		Set<VinDtoInfo> dtos = null;
		try {
			Vigneron entity = vigneronDao.findByContent(contentId);
			Set<Vin> vins = entity.getVins();
			dtos = new HashSet<VinDtoInfo>(vins.size());
			if(vins != null){
				for(Vin v : vins){
					VinDtoInfo dto = new VinDtoInfo();
					converter.convertEntityToDto(v, dto);
					dtos.add(dto);
				}
			}
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return dtos;
	}
	
	@Override
	@Cacheable(cacheName="vigneron")
	@Transactional
	public Set<VinDtoInfo> getVinsVigneronByKey(String key) throws CatalogException, TechnicalException {
		Set<VinDtoInfo> dtos = null;
		try {
			Vigneron entity = vigneronDao.findByContentKey(key);
			Set<Vin> vins = entity.getVins();
			dtos = new HashSet<VinDtoInfo>(vins.size());
			if(vins != null){
				for(Vin v : vins){
					VinDtoInfo dto = new VinDtoInfo();
					converter.convertEntityToDto(v, dto);
					dtos.add(dto);
				}
			}
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return dtos;
	}

	@Override
	@Cacheable(cacheName="vin")
	@Transactional
	public VinDto getVin(int vinId) throws CatalogException, TechnicalException {
		VinDto vin = new VinDto();
		try {
			Vin entity = vinDao.findOne(vinId);
			converter.convertEntityToDto(entity, vin);
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return vin;
	}
	
	@Override
	@Cacheable(cacheName="vin")
	@Transactional
	public VinDto getVinByContent(int contentId) throws CatalogException, TechnicalException {
		VinDto vin = new VinDto();
		try {
			Vin entity = vinDao.findByContent(contentId);
			converter.convertEntityToDto(entity, vin);
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return vin;
	}
	
	@Override
	@Cacheable(cacheName="vin")
	@Transactional
	public VinDto getVinByKey(String key) throws CatalogException, TechnicalException {
		VinDto vin = new VinDto();
		try {
			Vin entity = vinDao.findByContentKey(key);
			converter.convertEntityToDto(entity, vin);
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
		return vin;
	}
	
	@Override
	@Cacheable(cacheName="list")
	@Transactional
	public List<BeanDtoInfo> getList(int listId) throws CatalogException, TechnicalException {
		try {
			return getResults(listContentDao.findOne(listId));
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		} 
	}
	
	@Override
	@Cacheable(cacheName="list")
	@Transactional
	public List<BeanDtoInfo> getListByKey(String key) throws CatalogException, TechnicalException {
		try {
			return getResults(listContentDao.findByContentKey(key));
		} catch (Throwable e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		} 
	}
	
	public List<BeanDtoInfo> getResults(ListContent list) throws CatalogException, TechnicalException {
		try {
			List<BeanDtoInfo> results = new ArrayList<BeanDtoInfo>();
			for(ListQuery query : list.getQueries()) {
				if(query.getQuery() != null){
					SearchResultDto queryRes = searchManager.findByQuery(query.getQuery());	
					if (queryRes != null && queryRes.getResults() != null) results.addAll(queryRes.getResults());
				}
			}
			if(list.getSortParam() != null) {
				BeanComparator comparator = new BeanComparator(list.getSortParam());
				Collections.sort(results, comparator);	
				if("DESC".equals(list.getSortOrder())){
					Collections.reverse(results);
				}
			}
			if(list.getMax() != 0 &&  results.size() > list.getMax()){
				return results.subList(0, list.getMax());
			}
			return results;
		} catch (SearchException e) {
			throw new TechnicalException(TechnicalException.DATABASE, e);
		}
	}
	
	/**
	 * Franchement moche, mais en attendant ca fait le job
	 * */
	@Override
	@Cacheable(cacheName="content")
	@Transactional
	public BeanDtoInfo getContent(int contentId) throws CatalogException, TechnicalException {
		VigneronDto v = getVigneronByContent(contentId);
		if(v != null){
			return v;
		} else {
			return getVinByContent(contentId);
		}
	}
	
	
	/**
	 * Franchement moche, mais en attendant ca fait le job
	 * */
	@Override
	@Cacheable(cacheName="content")
	@Transactional
	public BeanDtoInfo getContentByKey(String key) throws CatalogException, TechnicalException {
		VigneronDto v = getVigneronByKey(key);
		if(v != null){
			return v;
		} else {
			return getVinByKey(key);
		}
	}

	public DtoFactory getDtoFactory() {
		return dtoFactory;
	}


	public void setDtoFactory(DtoFactory dtoFactory) {
		this.dtoFactory = dtoFactory;
	}


	public EntityDtoConverter getConverter() {
		return converter;
	}


	public void setConverter(EntityDtoConverter converter) {
		this.converter = converter;
	}
	
	public SearchManager getSearchManager() {
		return searchManager;
	}
	
	public void setSearchManager(SearchManager searchManager) {
		this.searchManager = searchManager;
	}
	
}
